IOS 特定於裝置的開發:基於加速計的滾動檢視

haibo wang發表於2014-12-23

    傾斜滾輪使用裝置的內建加速計來控制在UIScrollView的內容周圍移動。當使用者調增裝置時,材料會相應的下落,他不會把檢視定位在螢幕上,而是把內容檢視滾動到一個新的偏移位置。

   建立這個介面的挑戰在於:確定裝置在什麼地方應該具有他的靜止軸,大多數人最初建議當螢幕靠在他的背部應該是穩定的,並且z軸方向筆直的指向上方。事實證明:這實際是一個相當糟糕的設計選擇。要使用那根軸,就意味著導航期間螢幕必須實際的偏離觀看者。隨著裝置旋轉離開檢視,使用者將不能完全看到螢幕上所發生的事情,尤其是在固定的位置使用裝置時,站在高處看裝置有時會產生這種效果。

   作為代替,下面程式假定穩定的位置是通過z軸指向大約45度的方向,即使用者把iPhone或iPad窩在手中的自然位置,這處於正面朝上和正面朝前方的中間位置。從這個歪斜的位置來回傾斜,使螢幕在調整期間保持最大的可見性。

   這個程式中的另一處改變是低得多加速常量。這使螢幕上的運動能夠更慢的發生,讓使用者更容易降低速度並恢復導航。

#define SIGN(_NUM_) ((_NUM_ < 0) ? (-1) : 1) 
#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__) 
#define MAX(A,B) __NSMAX_IMPL__(A,B,__COUNTER__)

@implementation TestBedViewController
{
    UIScrollView *sv;
    
    float xoff;
    float xaccel;
    float xvelocity;
    
    float yoff;
    float yaccel;
    float yvelocity;
}

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    // extract the acceleration components
    float xx = -acceleration.x;
    float yy = (acceleration.z + 0.5f) * 2.0f; // between face up and face forward
    
    // Has the direction changed?
    float accelDirX = SIGN(xvelocity) * -1.0f; 
    float newDirX = SIGN(xx);
    float accelDirY = SIGN(yvelocity) * -1.0f;
    float newDirY = SIGN(yy);
    
    // Accelerate. To increase viscosity lower the additive value
    if (accelDirX == newDirX) xaccel = (abs(xaccel) + 0.005f) * SIGN(xaccel);
    if (accelDirY == newDirY) yaccel = (abs(yaccel) + 0.005f) * SIGN(yaccel);
    
    // Apply acceleration changes to the current velocity
    xvelocity = -xaccel * xx;
    yvelocity = -yaccel * yy;
}

- (void) tick
{
    xoff += xvelocity;
    xoff = MIN(xoff, 1.0f);
    xoff = MAX(xoff, 0.0f);
    
    yoff += yvelocity;
    yoff = MIN(yoff, 1.0f);
    yoff = MAX(yoff, 0.0f);
    
    CGFloat xsize = sv.contentSize.width - sv.frame.size.width;
    CGFloat ysize = sv.contentSize.height - sv.frame.size.height;
    sv.contentOffset = CGPointMake(xoff * xsize, yoff * ysize);
}

- (void) loadView
{
    [super loadView];
    sv = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    self.view = sv;
}

- (void) viewDidAppear:(BOOL)animated
{
    NSString *map = @"http://maps.weather.com/images/maps/current/curwx_720x486.jpg";
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:
     ^{
         // Load the weather data
         NSURL *weatherURL = [NSURL URLWithString:map];
         NSData *imageData = [NSData dataWithContentsOfURL:weatherURL];
         
         // Update the image on the main thread using the main queue
         [[NSOperationQueue mainQueue] addOperationWithBlock:^{
             UIImage *weatherImage = [UIImage imageWithData:imageData];
             UIImageView *imageView = [[UIImageView alloc] initWithImage:weatherImage];
             CGSize initSize = weatherImage.size;
             CGSize destSize = weatherImage.size;
             
             while ((destSize.width < (self.view.frame.size.width * 4)) ||
                    (destSize.height < (self.view.frame.size.height * 4)))
             {
                 destSize.width += initSize.width;
                 destSize.height += initSize.height;
             }
             
             imageView.userInteractionEnabled = NO;
             imageView.frame = (CGRect){.size = destSize};
             sv.contentSize = destSize;
             
             [sv addSubview:imageView];
             
             // Activate the accelerometer
             [[UIAccelerometer sharedAccelerometer] setDelegate:self];
             
             // Start the physics timer
             [NSTimer scheduledTimerWithTimeInterval: 0.03f target: self selector: @selector(tick) userInfo: nil repeats: YES];
         }];
     }];
}

 

相關文章